/**
 * @program: PavtFirmware
 * @description:
 * @author: zyc
 * @create: 2018-07-04 09:25
 **/
package com.zjpavt.client.hal;

import com.zjpavt.client.Client;
import com.zjpavt.client.Connection;
import com.zjpavt.client.util.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *         如果文件获取到的UUID为空或者不符合规范 则无视本次获取.
 *      1. 如果当前对象的ID为null 则将文件的ID设置到本类.
 *      2. 如果当前对象的ID已经有值 则输出异常 且把文件的ID设置到本类 检测Connection.deviceID是否为null 如果是则设置.
 *
 *
 *      3. Connection 发起连接后且已经获取到ID后，不再来本类获取设备ID而是在Connection内缓存一份设备ID
 *      4. 当Connection发起重连的时候需要重新获取设备ID.  在setchannel中每次更新Connection的时候刷新设备ID
 *      5. 页面显示和X指令的deviceID以Connection中的设备ID为准
 *      6. connection没有设备ID的时候,X指令和页面显示 会触发获取文件中的设备ID.
 *
 * @Author zyc
 */
@Slf4j
public class DeviceID {
    /**
     * 设备ID
     * 由于缓存 当获取到不同的ID后 触发ID切换事件
     */
    private String idString;
    /**
     * 设备id的UUID格式,不太改变，直接存UUID提高效率
     */
    private UUID uuidForDeviceId;
    private final static String UUID_REGEX = "[0-9a-f]{32}";
    private final static String UUID_SECTION_REGEX = "([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})";
    private final static String UUID_SWITCH_REGEX = "$1-$2-$3-$4-$5";
    private final static Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
    private static final DeviceID INSTANCE = new DeviceID();
    public static final String CONFIG_FILE = ClientConfig.CONFIG_NAME;
    /**
     * 设备属性读取
     */
    public static final String INI_DEVICE_ID_SELECT = "id_data";
    public static DeviceID getINSTANCE() {
        return INSTANCE;
    }

    private DeviceID() {
        tryGetDeviceIDFromFile();
    }

    /**
     * 从文件读取设备ID
     * 同时只能有一个线程去取ID
     *
     * @Return 返回符合UUID格式的ID 否则返回null
     */
    private synchronized String tryGetDeviceIDFromFile() {
        try {
//            去除行首行末空格,因该不会有
            String propertiesDeviceId = PropertiesUtil.getProperties(CONFIG_FILE,Consts.KEY_DEVICE_ID);
            if (propertiesDeviceId == null) {
                return null;
            }
            Matcher uuidMatcher = UUID_PATTERN.matcher(propertiesDeviceId);
//          获取到额设备是一个UUID格式的字符串
            if (uuidMatcher.matches()) {
//                已经获取到过设备ID
                if (this.idString != null) {
                    if (!propertiesDeviceId.equals(this.idString)) {
//                    获取到设备ID不相等，修改本方法的设备ID，修改的同时通知Connection发生改变。
                        setDeviceIdString(propertiesDeviceId);
                    } else {
//                        获取到一致的设备ID do nothing
                    }
//              第一次获取到设备ID
                } else {
                    setDeviceIdString(propertiesDeviceId);
                }
                return propertiesDeviceId;
            } else {
                log.error("try get deviceID:" + propertiesDeviceId + " not accept by " + UUID_REGEX);
                return null;
            }
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
            this.idString = null;
        }
        return null;
    }


    /**
     * 从文件中获取设备ID设置到程序中
     * @param idString
     */
    public void setDeviceIdString(String idString) {
        if (idString == null || idString.equals(this.idString)) {
            return;
        }
        String uuidFormat = idString.replaceAll(UUID_SECTION_REGEX, UUID_SWITCH_REGEX);
        try {
            setDeviceId(UUID.fromString(uuidFormat));
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * 每次设备ID发生变更的时候 都会通过这个方法
     * 且都需要通知Connection.setDeviceId方法，但是Connection如果已经存在设备ID了 会无视
     * @param id
     */
    public void setDeviceId(UUID id) {
        if (id != null && !id.equals(this.uuidForDeviceId)) {
            log.info("set device idString from  to "+id);
            this.uuidForDeviceId = id;
            Connection.getInstance().notifyDeviceIDChanged(uuidForDeviceId);
//            当id为null 或者 没有发生改变
        } else {
            return;
        }
    }

    /**
     * 扫码枪重新设置设备ID，并且存到设备文件中
     * @param id
     */
    public void changeDeviceId(UUID id) {
        if (id != null && !id.equals(this.uuidForDeviceId)) {
            log.info("change device idString from  to "+id);
            this.uuidForDeviceId = id;
            Connection.getInstance().notifyDeviceIDChanged(uuidForDeviceId);
            try {
                PropertiesUtil.setPropertyOrReplace(CONFIG_FILE,Consts.KEY_DEVICE_ID,id.toString().replaceAll("-",""));
            } catch (IOException e) {
                log.error(LoggerUtil.convertException2String(e));
            }
//            当id为null 或者 没有发生改变
        } else {
            return;
        }
    }

    /**
     * 每次直接从ini文件中获取设备ID的值
     * @return
     */
    public synchronized String getIdString() {
        tryGetDeviceIDFromFile();
        return this.idString;
    }

    /**
     * 每次都会去冲文件刷新一次，然后返回设备ID
     * 返回一个设备的UUID
     *
     * 可能为null
     * @return
     */
    public synchronized UUID getUUID() {
        tryGetDeviceIDFromFile();
        return this.uuidForDeviceId;
    }

    /**
     * 获取缓存的设备ID 如果为null则尝试去文件中获取
     * @return
     */
    public String getDeviceIdCached() {
        if (this.idString == null) {
            if (uuidForDeviceId != null) {
                return this.uuidForDeviceId.toString().replaceAll("-","");
            }

            tryGetDeviceIDFromFile();
        }
        return this.idString;
    }
    /**
     * 获取缓存的设备ID 如果为null则尝试去文件中获取
     * @return
     */
    public UUID getUUIDCached() {
        if (this.uuidForDeviceId == null) {
            tryGetDeviceIDFromFile();
        }
        return this.uuidForDeviceId;
    }

    /**
     * 将字符串转化成UUID 不成功返回null
     * @param idString
     * @return
     */
    public static UUID string2uuid(String idString) {
        String uuidFormat = idString.replaceAll(UUID_SECTION_REGEX, UUID_SWITCH_REGEX);
        try {
            return UUID.fromString(uuidFormat);
        } catch (Exception e) {
            return null;
        }

    }

}
